package dmxGroupControl.solvers;

import dmxGroupControl.DMXFixture;
import dmxGroupControl.Fixture;
import dmxGroupControl.LightController;
import dmxGroupControl.Universe;
import javafx.scene.paint.Color;
import org.jdom2.Element;

import java.awt.color.ColorSpace;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;

/**
 * Created by higginsonj on 9/03/2015.
 */
public class ColorWheelSolver extends HSBSolver
{
    static List<String> solvableChannels = Arrays.asList("ColorWheel", "Master");

    ColorWheel wheel;

    public ColorWheelSolver(Fixture fixture)
    {
        super(fixture);
        wheel = new ColorWheel(fixture.GetName());
    }

    @Override
    public String GetType()
    {
        return "colorwheel";
    }

    @Override
    public boolean SolvesAttribute(String attribute)
    {
        return SolvesAttribute(attribute, solvableAttributes);
    }

    public static boolean GetIsApplicableToFixture(Fixture fixture)
    {
        return Solver.GetIsApplicableToFixture(fixture, Arrays.asList("ColorWheel"));
    }

    @Override
    protected void SolveInternal()
    {
        Color color;
        if(IsOverrideEnabled())
        {
            color = Color.hsb(overrideHue * 360.0f, overrideSaturation, overrideBrightness);
        }
        else
        {
            color = Color.hsb(hue * 360.0f, saturation, brightness);
        }

        ColorWheel.ColorWheelEntry entry = wheel.GetClosestEntry(color);

        fixture.GetChannels().OptionalSetChannel("ColorWheel", entry.beginning / 256.0f);
        fixture.GetChannels().OptionalSetChannel("Master", brightness);
        if(fixture instanceof DMXFixture)
        {
            Color colorToPaint = new Color(entry.getColor().getRed() * brightness,
                                           entry.getColor().getGreen() * brightness,
                                           entry.getColor().getBlue() * brightness,
                                           1.0f);

            ((DMXFixture)fixture).SetDisplayColor(colorToPaint);
        }

    }

    class ColorWheel
    {
        ArrayList<ColorWheelEntry> entries;

        public ColorWheel(String fixtureType)
        {
            entries = new ArrayList<>();
            Element colorWheelDefinition = LightController.GetColorWheelDefinition(fixtureType);
            for(Element colorEntry : colorWheelDefinition.getChildren("color"))
            {
                int beginning = Integer.parseInt(colorEntry.getAttributeValue("min"));
                int ending = Integer.parseInt(colorEntry.getAttributeValue("max"));
                Color color = Color.web(colorEntry.getAttributeValue("rgb"));

                ColorWheelEntry entry = new ColorWheelEntry(beginning, ending, color);
                entries.add(entry);
            }
        }

        public ColorWheelEntry GetClosestEntry(Color color)
        {
            double targetHue = color.getHue();

            ColorWheelEntry closest = entries.get(0);
            double closestDistance = ColorDistance(color, closest.getColor());

            for(ColorWheelEntry entry : entries)
            {
                double currentHue = entry.getColor().getHue();
                double distance = ColorDistance(color, entry.getColor());
                if(distance < closestDistance)
                {
                    closestDistance = distance;
                    closest = entry;
                }
            }

            return closest;
        }

        class ColorWheelEntry
        {
            public int getBeginning()
            {
                return beginning;
            }

            public int getEnding()
            {
                return ending;
            }

            public Color getColor()
            {
                return color;
            }

            int beginning;
            int ending;
            Color color;

            public ColorWheelEntry(int beginning, int ending, Color color)
            {
                this.beginning = beginning;
                this.ending = ending;
                this.color = color;
            }
        }
    }

    private static double ColorDistance(Color color1, Color color2)
    {
        ColorSpace labColorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);

        float[] color1lab = labColorSpace.toCIEXYZ(new float[] { (float)color1.getRed(), (float)color1.getGreen(), (float)color1.getBlue() });
        float[] color2lab = labColorSpace.toCIEXYZ(new float[] { (float)color2.getRed(), (float)color2.getGreen(), (float)color2.getBlue() });

        float dx = color1lab[0] - color2lab[0];
        float dy = color1lab[1] - color2lab[1];
        float dz = color1lab[2] - color2lab[2];

        // We should avoid Math.pow or Math.hypot due to perfomance reasons
        return Math.sqrt(dx * dx + dy * dy + dz * dz);


        /*double rmean = (e1.getRed() + e2.getRed() ) / 2;
        double r = e1.getRed() - e2.getRed();
        double g = e1.getGreen() - e2.getGreen();
        double b = e1.getBlue() - e2.getBlue();


        return sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8));*/
    }
}


